package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.ChromeOSOnly
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.ArrayBuffer
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.api.types.Value2Function
import io.shuttle.mbe.api.types.Value3Function
import io.shuttle.mbe.api.types.VoidFunction
import io.shuttle.mbe.core.Promise
import java.util.Date

////////////////////
// File System Provider
////////////////////
/**
 * Use the `Browser.fileSystemProvider` API to create file systems, that can be accessible from the file manager on Chrome OS.
 *
 * Permissions: "fileSystemProvider"
 * @platform ChromeOS only
 */
@ChromeOSOnly
interface FileSystemProvider {
    // Returns information about a file system with the passed fileSystemId.
    @PromiseStyleMinVersion(96)
    fun get(
        fileSystemId: String,
        callback: Value1Function<FileSystemInfo>? = null
    ): Promise<FileSystemInfo>

    // Returns all file systems mounted by the extension.
    @PromiseStyleMinVersion(96)
    fun getAll(callback: Value1Function<List<FileSystemInfo>>? = null): Promise<List<FileSystemInfo>>

    // Mounts a file system with the given fileSystemId and displayName.
    // displayName will be shown in the left panel of the Files app.
    // displayName can contain any characters including '/', but cannot be an empty string.
    // displayName must be descriptive but doesn't have to be unique.
    // The fileSystemId must not be an empty string.
    //
    //Depending on the type of the file system being mounted, the source option must be set appropriately.
    //
    //In case of an error, runtime.lastError will be set with a corresponding error code.
    @PromiseStyleMinVersion(96)
    fun mount(option: MountOptions, callback: VoidFunction? = null): Promise<Void>

    // Notifies about changes in the watched directory at observedPath in recursive mode.
    // If the file system is mounted with supportsNotifyTag, then tag must be provided,
    // and all changes since the last notification always reported, even if the system was shutdown.
    // The last tag can be obtained with getAll.
    //
    //To use, the file_system_provider.notify manifest option must be set to true.
    //
    //Value of tag can be any string which is unique per call, so it's possible to identify the last registered notification.
    // Eg. if the providing extension starts after a reboot, and the last registered notification's tag is equal to "123",
    // then it should call notify for all changes which happened since the change tagged as "123".
    // It cannot be an empty string.
    //
    //Not all providers are able to provide a tag, but if the file system has a changelog, then the tag can be eg.
    // a change number, or a revision number.
    //
    //Note that if a parent directory is removed, then all descendant entries are also removed, and if they are watched, then the API must be notified about the fact.
    // Also, if a directory is renamed, then all descendant entries are in fact removed, as there is no entry under their original paths anymore.
    //
    //In case of an error, runtime.lastError will be set will a corresponding error code.
    @ChromeMinVersion(45)
    @PromiseStyleMinVersion(96)
    fun notify(option: NotifyOptions, callback: VoidFunction? = null): Promise<Void>

    // Unmounts a file system with the given fileSystemId.
    // It must be called after onUnmountRequested is invoked.
    // Also, the providing extension can decide to perform unmounting if not requested (eg. in case of lost connection, or a file error).
    //
    //In case of an error, runtime.lastError will be set with a corresponding error code.
    @PromiseStyleMinVersion(96)
    fun unmount(options: UnmountOptions, callback: VoidFunction? = null): Promise<Void>

    // Raised when aborting an operation with operationRequestId is requested.
    // The operation executed with operationRequestId must be immediately stopped and successCallback of this abort request executed.
    // If aborting fails, then errorCallback must be called. Note,
    // that callbacks of the aborted operation must not be called, as they will be ignored.
    // Despite calling errorCallback, the request may be forcibly aborted.
    // @return {callback.errorCallback.error}
    val onAbortRequested: Events.Event<Value3Function<AbortRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when setting a new directory watcher is requested.
    // If an error occurs, then errorCallback must be called.
    @ChromeMinVersion(45)
    val onAddWatcherRequested: Events.Event<Value3Function<AddWatcherRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when opening a file previously opened with openRequestId is requested to be closed.
    val onCloseFileRequested: Events.Event<Value3Function<CloseFileRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when showing a configuration dialog for fileSystemId is requested.
    // If it's handled, the file_system_provider.configurable manfiest option must be set to true.
    @ChromeMinVersion(44)
    val onConfigureRequested: Events.Event<Value3Function<ConfigureRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when copying an entry (recursively if a directory) is requested.
    // If an error occurs, then errorCallback must be called.
    val onCopyEntryRequested: Events.Event<Value3Function<CopyEntryRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when creating a directory is requested. The operation must fail with the EXISTS error if the target directory already exists.
    // If recursive is true, then all of the missing directories on the directory path must be created.
    val onCreateDirectoryRequested: Events.Event<Value3Function<CreateDirectoryRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when creating a file is requested.
    // If the file already exists, then errorCallback must be called with the "EXISTS" error code.
    val onCreateFileRequested: Events.Event<Value3Function<CreateFileRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when deleting an entry is requested.
    // If recursive is true, and the entry is a directory, then all of the entries inside must be recursively deleted as well.
    val onDeleteEntryRequested: Events.Event<Value3Function<DeleteEntryRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when executing an action for a set of files or directories is\ requested. After the action is completed, successCallback must be called. On error, errorCallback must be called.
    @ChromeMinVersion(48)
    val onExecuteActionRequested: Events.Event<Value3Function<ExecuteActionRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when a list of actions for a set of files or directories at entryPaths is requested.
    // All of the returned actions must be applicable to each entry.
    // If there are no such actions, an empty array should be returned.
    // The actions must be returned with the successCallback call.
    // In case of an error, errorCallback must be called.
    @ChromeMinVersion(48)
    val onGetActionsRequested: Events.Event<Value3Function<GetActionsRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when metadata of a file or a directory at entryPath is requested.
    // The metadata must be returned with the successCallback call.
    // In case of an error, errorCallback must be called.
    val onGetMetadataRequested: Events.Event<Value3Function<GetMetadataRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when showing a dialog for mounting a new file system is requested.
    // If the extension/app is a file handler, then this event shouldn't be handled.
    // Instead app.runtime.onLaunched should be handled in order to mount new file systems when a file is opened.
    // For multiple mounts, the file_system_provider.multiple_mounts manifest option must be set to true.
    val onMountRequested: Events.Event<Value2Function<VoidFunction, Value1Function<ProviderError>>>

    // Raised when moving an entry (recursively if a directory) is requested.
    // If an error occurs, then errorCallback must be called.
    val onMoveEntryRequested: Events.Event<Value3Function<MoveEntryRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when opening a file at filePath is requested.
    // If the file does not exist, then the operation must fail.
    // Maximum number of files opened at once can be specified with MountOptions.
    val onOpenFileRequested: Events.Event<Value3Function<OpenFileRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when contents of a directory at directoryPath are requested.
    // The results must be returned in chunks by calling the successCallback several times.
    // In case of an error, errorCallback must be called.
    val onReadDirectoryRequested: Events.Event<Value3Function<ReadDirectoryRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when reading contents of a file opened previously with openRequestId is requested.
    // The results must be returned in chunks by calling successCallback several times.
    // In case of an error, errorCallback must be called.
    val onReadFileRequested: Events.Event<Value3Function<ReadFileRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when the watcher should be removed. If an error occurs, then errorCallback must be called.
    @ChromeMinVersion(45)
    val onRemoveWatcherRequested: Events.Event<Value3Function<RemoveWatcherRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when truncating a file to a desired length is requested. If an error occurs, then errorCallback must be called
    val onTruncateRequested: Events.Event<Value3Function<TruncateRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when unmounting for the file system with the fileSystemId identifier is requested.
    // In the response, the unmount API method must be called together with successCallback.
    // If unmounting is not possible (eg. due to a pending operation), then errorCallback must be called.
    val onUnmountRequested: Events.Event<Value3Function<UnmountRequestedOptions, VoidFunction, Value1Function<ProviderError>>>

    // Raised when writing contents to a file opened previously with openRequestId is requested.
    val onWriteFileRequested: Events.Event<Value3Function<WriteFileRequestedOptions, VoidFunction, Value1Function<ProviderError>>>


    data class AbortRequestedOptions(
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // An ID of the request to be aborted.
        var operationRequestId: Number,
        // The unique identifier of this request.
        var requestId: Number
    )

    @ChromeMinVersion(45)
    data class Action(
        // The identifier of the action. Any string or CommonActionId for common actions.
        var id: String,
        // The title of the action. It may be ignored for common actions.
        var title: String?,
    )

    data class AddWatcherRequestedOptions(
        // The path of the entry to be observed.
        var entryPath: String,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // Whether observing should include all child entries recursively. It can be true for directories only.
        var recursive: Boolean,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class Change(
        // The type of the change which happened to the entry.
        var changeType: ChangeType,
        // Information relating to the file if backed by a cloud file system.
        @ChromeMinVersion(125)
        var cloudFileInfo: CloudFileInfo,
        // The path of the changed entry.
        var entryPath: String,
    )

    // Type of a change detected on the observed directory.
    enum class ChangeType {
        CHANGED,
        DELETED
    }

    data class CloseFileRequestedOptions(
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // A request ID used to open the file.
        var openRequestId: Number,
        // The unique identifier of this request.
        var requestId: Number,
    )

    @ChromeMinVersion(125)
    data class CloudFileInfo(
        // A tag that represents the version of the file.
        var versionTag: String?,
    )

    @ChromeMinVersion(117)
    data class CloudIdentifier(
        // The provider's identifier for the given file/directory.
        var id: String,
        // Identifier for the cloud storage provider (e.g. 'drive.google.com').
        var providerName: String,
    )

    // List of common actions. "SHARE" is for sharing files with others.
    // "SAVE_FOR_OFFLINE" for pinning (saving for offline access).
    // "OFFLINE_NOT_NECESSARY" for notifying that the file doesn't need to be stored for offline access anymore.
    // Used by onGetActionsRequested and onExecuteActionRequested.
    @ChromeMinVersion(45)
    enum class CommonActionId {
        SAVE_FOR_OFFLINE,
        OFFLINE_NOT_NECESSARY,
        SHARE
    }

    @ChromeMinVersion(44)
    data class ConfigureRequestedOptions(
        // The identifier of the file system to be configured.
        var fileSystemId: String,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class CopyEntryRequestedOptions(
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // The unique identifier of this request.
        var requestId: Number,
        // The source path of the entry to be copied.
        var sourcePath: String,
        // The destination path for the copy operation.
        var targetPath: String,
    )

    data class CreateDirectoryRequestedOptions(
        // The path of the directory to be created.
        var directoryPath: String,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // Whether the operation is recursive (for directories only).
        var recursive: Boolean,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class CreateFileRequestedOptions(
        // The path of the file to be created.
        var filePath: String,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class DeleteEntryRequestedOptions(
        // The path of the entry to be deleted.
        var entryPath: String,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // Whether the operation is recursive (for directories only).
        var recursive: Boolean,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class EntryMetadata(
        // nformation that identifies a specific file in the underlying cloud file system.
        // Must be provided if requested in options and the file is backed by cloud storage.
        @ChromeMinVersion(125)
        var cloudFileInfo: CloudFileInfo?,
        // Cloud storage representation of this entry.
        // Must be provided if requested in options and the file is backed by cloud storage.
        // For local files not backed by cloud storage, it should be undefined when requested.
        @ChromeMinVersion(117)
        var cloudIdentifier: CloudIdentifier?,
        // True if it is a directory. Must be provided if requested in options.
        var isDirectory: Boolean?,
        // Mime type for the entry. Always optional, but should be provided if requested in options.
        var mimeType: String?,
        // The last modified time of this entry. Must be provided if requested in options.
        // FIXME: to js Date
        var modificationTime: Date?,
        // Name of this entry (not full path name). Must not contain '/'.
        // For root it must be empty.
        // Must be provided if requested in options.
        var name: String?,
        // File size in bytes. Must be provided if requested in options.
        var size: Number?,
        // Thumbnail image as a data URI in either PNG, JPEG or WEBP format, at most 32 KB in size.
        // Optional, but can be provided only when explicitly requested by the onGetMetadataRequested event.
        var thumbnail: String?,
    )

    @ChromeMinVersion(45)
    data class ExecuteActionRequestedOptions(
        // The identifier of the action to be executed.
        var actionId: String,
        // The set of paths of the entries to be used for the action.
        @ChromeMinVersion(47)
        var entryPaths: List<String>,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class FileSystemInfo(
        // A human-readable name for the file system.
        var displayName: String,
        // The identifier of the file system.
        var fileSystemId: String,
        // List of currently opened files.
        var openedFiles: List<OpenedFile>,
        // The maximum number of files that can be opened at once. If 0, then not limited.
        var openedFilesLimit: Number,
        // Whether the file system supports the tag field for observing directories.
        @ChromeMinVersion(45)
        var supportsNotifyTag: Boolean?,
        // List of watchers.
        @ChromeMinVersion(45)
        var watchers: List<Watcher>,
        // Whether the file system supports operations which may change contents of the file system (such as creating,
        // deleting or writing to files).
        var writable: Boolean,
    )

    @ChromeMinVersion(45)
    data class GetActionsRequestedOptions(
        // List of paths of entries for the list of actions.
        @ChromeMinVersion(47)
        var entryPaths: List<String>,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class GetMetadataRequestedOptions(
        // Set to true if cloudFileInfo value is requested.
        @ChromeMinVersion(125)
        var cloudFileInfo: Boolean,
        // Set to true if cloudIdentifier value is requested.
        @ChromeMinVersion(117)
        var cloudIdentifier: Boolean,
        // The path of the entry to fetch metadata about.
        var entryPath: String,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // Set to true if is_directory value is requested.
        @ChromeMinVersion(49)
        var isDirectory: Boolean,
        // Set to true if mimeType value is requested.
        @ChromeMinVersion(49)
        var mimeType: Boolean,
        // Set to true if modificationTime value is requested.
        @ChromeMinVersion(49)
        var modificationTime: Boolean,
        // Set to true if name value is requested.
        @ChromeMinVersion(49)
        var name: Boolean,
        // The unique identifier of this request.
        var requestId: Number,
        // Set to true if size value is requested.
        @ChromeMinVersion(49)
        var size: Boolean,
        // Set to true if thumbnail value is requested.
        var thumbnail: Boolean,
    )

    data class MountOptions(
        // A human-readable name for the file system.
        var displayName: String,
        // The string indentifier of the file system. Must be unique per each extension.
        var fileSystemId: String,
        // The maximum number of files that can be opened at once. If not specified, or 0, then not limited.
        var openedFilesLimit: Number?,
        // Whether the framework should resume the file system at the next sign-in session. True by default.
        @ChromeMinVersion(64)
        var persistent: Boolean?,
        // Whether the file system supports the tag field for observed directories.
        @ChromeMinVersion(45)
        var supportsNotifyTag: Boolean?,
        // Whether the file system supports operations which may change contents of the file system (such as creating, deleting or writing to files).
        var writable: Boolean?,
    )

    data class MoveEntryRequestedOptions(
        // The string indentifier of the file system. Must be unique per each extension.
        var fileSystemId: String,
        // The unique identifier of this request.
        var requestId: Number,
        // The source path of the entry to be moved into a new place.
        var sourcePath: String,
        // The destination path for the copy operation.
        var targetPath: String,
    )

    data class NotifyOptions(
        // The type of the change which happened to the observed entry.
        // If it is DELETED, then the observed entry will be automatically removed from the list of observed entries.
        var changeType: String,
        // List of changes to entries within the observed directory (including the entry itself)
        var changes: List<Change>,
        // The string indentifier of the file system. Must be unique per each extension.
        var fileSystemId: String,
        // The path of the observed entry.
        var observedPath: String,
        // Mode of the observed entry.
        var recursive: Boolean,
        // Tag for the notification. Required if the file system was mounted with the supportsNotifyTag option.
        // Note, that this flag is necessary to provide notifications about changes which changed even when the system was shutdown.
        var tag: String?,
    )

    data class OpenedFile(
        // The path of the opened file.
        var filePath: String,
        // Whether the file was opened for reading or writing.
        var mode: OpenFileMode,
        // A request ID to be be used by consecutive read/write and close requests.
        var openRequestId: Number,
    )

    // Mode of opening a file. Used by onOpenFileRequested.
    enum class OpenFileMode {
        READ,
        WRITE,
    }

    data class OpenFileRequestedOptions(
        // The path of the opened file.
        var filePath: String,
        // The string indentifier of the file system. Must be unique per each extension.
        var fileSystemId: String,
        // Whether the file was opened for reading or writing.
        var mode: OpenFileMode,
        // The unique identifier of this request.
        var requestId: Number,
    )

    // Error codes used by providing extensions in response to requests as well as in case of errors when calling methods of the API.
    // For success, "OK" must be used.
    enum class ProviderError {
        OK,
        FAILED,
        IN_USE,
        EXISTS,
        NOT_FOUND,
        ACCESS_DENIED,
        TOO_MANY_OPENED,
        NO_MEMORY,
        NO_SPACE,
        NOT_A_DIRECTORY,
        INVALID_OPERATION,
        SECURITY,
        ABORT,
        NOT_A_FILE,
        NOT_EMPTY,
        INVALID_URL,
        IO
    }

    data class ReadDirectoryRequestedOptions(
        // The path of the directory which contents are requested.
        var directoryPath: String,
        // The string indentifier of the file system. Must be unique per each extension.
        var fileSystemId: String,
        // Set to true if is_directory value is requested.
        @ChromeMinVersion(49)
        var isDirectory: Boolean,
        // Set to true if mimeType value is requested.
        @ChromeMinVersion(49)
        var mimeType: Boolean,
        // Set to true if modificationTime value is requested.
        @ChromeMinVersion(49)
        var modificationTime: Boolean,
        // Set to true if name value is requested.
        @ChromeMinVersion(49)
        var name: Boolean,
        // The unique identifier of this request.
        var requestId: Number,
        // Set to true if size value is requested.
        @ChromeMinVersion(49)
        var size: Boolean,
        // Set to true if thumbnail value is requested.
        var thumbnail: Boolean,
    )

    data class ReadFileRequestedOptions(
        // The string indentifier of the file system. Must be unique per each extension.
        var fileSystemId: String,
        // Number of bytes to be returned.
        var length: Number,
        // Position in the file (in bytes) to start reading from.
        var offset: Number,
        // A request ID used to open the file.
        var openRequestId: Number,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class RemoveWatcherRequestedOptions(
        // The path of the entry to fetch metadata about.
        var entryPath: String,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // Mode of the watcher.
        var recursive: Boolean,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class TruncateRequestedOptions(
        // The path of the file to be truncated.
        var filePath: String,
        // The identifier of the file system related to this operation.
        var fileSystemId: String,
        // Number of bytes to be retained after the operation completes.
        var length: Number,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class UnmountOptions(
        // The identifier of the file system to be unmounted.
        var fileSystemId: String,
    )

    data class UnmountRequestedOptions(
        //  // The identifier of the file system to be unmounted.
        var fileSystemId: String,
        // The unique identifier of this request.
        var requestId: Number,
    )

    data class Watcher(
        // The path of the entry being observed.
        var entryPath: String,
        // Tag used by the last notification for the watcher.
        var lastTag: String?,
        // Whether watching should include all child entries recursively. It can be true for directories only.
        var recursive: Boolean,
    )

    data class WriteFileRequestedOptions(
        // Buffer of bytes to be written to the file.
        var data: ArrayBuffer,
        //  // The identifier of the file system to be unmounted.
        var fileSystemId: String,
        // Position in the file (in bytes) to start writing the bytes from.
        var offset: Number,
        // A request ID used to open the file.
        var openRequestId: Number,
        // The unique identifier of this request.
        var requestId: Number,
    )
}